home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / util / tracker.1 < prev   
Internet Message Format  |  1989-02-01  |  18KB

  1. Path: xanth!ukma!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i007:  tracker - resource tracking routines
  5. Message-ID: <11498@swan.ulowell.edu>
  6. Date: 1 Feb 89 05:01:31 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 539
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: karl@sugar.uu.net (Karl Lehenbauer)
  12. Posting-number: Volume 89, Issue 7
  13. Archive-name: util/tracker.1
  14.  
  15. Even though in an alpha state, I think this will be of great use to
  16. Aztec C programmers who want to be sure they're freeing all of their
  17. memory.  It also helps locate where the problem is; it prints out the
  18. file name and line number where each AllocMem was performed that did
  19. not have a matching FreeMem, for example.
  20.  
  21. #    This is a shell archive.
  22. #    Remove everything above and including the cut line.
  23. #    Then run the rest of the file through sh.
  24. #----cut here-----cut here-----cut here-----cut here----#
  25. #!/bin/sh
  26. # shar:    Shell Archiver
  27. #    Run the following text with /bin/sh to create:
  28. #    README
  29. #    tracker.h
  30. #    tracker.c
  31. # This archive created: Mon Jan 30 17:24:47 1989
  32. cat << \SHAR_EOF > README
  33. C Programmer's Amiga Resource Tracking Routines    Version 0.0a   1/5/89
  34. -------------------------------------------------------------------------
  35.  
  36. This code and documentation is released to the Public Domain without any
  37. restrictions on use, resale or redistribution.
  38.  
  39. No license or warranty of appropriateness, usefulness or bug-freeness is
  40. expressed or implied.  This is free code.  We don't have a contract.
  41.  
  42. Written by:
  43.  
  44. Karl Lehenbauer, Hackercorp, 3918 Panorama, Missouri City, TX, USA  77459
  45. (713) 438-4964 voice,  (713) 438-5018 data
  46. Usenet: uunet!sugar!karl, Internet: karl@sugar.uu.net, BIX: kelehen
  47.  
  48.  
  49. These routines were written to aid C programmers in insuring that their
  50. programs are  properly returning all the memory, signals and locks they
  51. have allocated.  
  52.  
  53. To use them, include tracker.h in your C source programs and recompile.
  54. (The use of an "includes.c" along with the Manx +i and +h flags to
  55. precompile the symbol table obviates the necessity of editing 
  56. '#include "tracker.h">' into every one of your source files.)
  57. Next, edit your exit routine to call TrackerExitReport() after it has
  58. freed everything.  Then, compile tracker.c using the memory model you're 
  59. using for the rest of your code and link your program to include tracker.o.
  60. (This can all be done in your makefile if you've got it set up right.)
  61. Finally, run your program.
  62.  
  63. The program must either be initiated from the CLI or you must edit your 
  64. program's startup code to fopen stderr and direct it somewhere  (like 
  65. to a  window or a file) or you won't get any of the resource tracker's 
  66. messages, or worse.
  67.  
  68. As your program runs, every time it allocs memory via AllocMem(), allocs
  69. a signal via AllocSignal() or gets a Lock via Lock(), special tracking
  70. routines execute instead (thanks to some macros defined by tracker.h)
  71. which, in addition to performing the action you requested, record 
  72. information about what you requested and what you got.  For AllocMem(),
  73. the source file and line of the AllocMem call as well as the amount of memory
  74. requested and the pointer to the memory returned are recorded.  For
  75. AllocSignal(), only the signal numbers allocated are recorded at this time.
  76. For Lock(), the file name to be locked, source file and line number and 
  77. the lock returned are recorded.
  78.  
  79. When your program frees memory via FreeMem(), a special tracking version
  80. of FreeMem is executed that searches the list of entries recorded by the
  81. tracking version of AllocMem().  The resource tracker reports if you free
  82. something more than once, if you free something that you didn't allocate
  83. or if the length that you are freeing differes from what you allocated.
  84. This includes the source file name and line number of the matching AllocMem 
  85. (when it is known) and always includes the source file and line for FreeMem.
  86.  
  87. When your program frees a signal via FreeSignal(), a tracking version
  88. of FreeSignal checks to see if you have allocated the signal you are
  89. now freeing.  If you haven't, it reports it, but it doesn't include the
  90. file name and line number at this time.  I don't think this is a serious
  91. problem, as signals aren't as critical as the other stuff, but I may add
  92. it in a future version.
  93.  
  94. When your program unlocks a lock via UnLock(), a tracking version of UnLock
  95. searches the list of recorded locks to see if you locked the lock you are
  96. unlocking and report accordingly.
  97.  
  98. The tracker exit report provided by TrackerExitReport() is where most of
  99. the bugs are identified.  TrackerExitReport identifies all AllocMems that
  100. didn't have a corresponding FreeMem, including the source file and line
  101. of the call to AllocMem as well as the address and size of the memory
  102. in question.  The resource tracker does not free the memory for you because
  103. you may have not freed the memory on purpose (for example, you may have
  104. spawned a task that uses it will free it later) and it cannot know that.
  105.  
  106. The exit report details all signals that weren't freed.  This isn't very
  107. important, in my opinion.
  108.  
  109. Also, the exit report prints information on all file locks that were made
  110. that didn't have a corresponding UnLock.  This information includes the
  111. name of the file, value of the lock and the source file and line of the
  112. code that locked it.
  113.  
  114. The exit report also prints the number of calls to allocate and free memory,
  115. allocate and free signals and to lock and unlock files as a gross indicator
  116. of whether you're cleaning everything up properly.
  117.  
  118. Note that, in the default configuration, memory that is freed and 
  119. reallocated will screw up the tracker because the tracker continues 
  120. to track memory objects after they have been  freed. This is a tradeoff 
  121. between being to be able to detect multiple frees of the same memory or 
  122. not.  If that's a problem, tracker.c can be recompiled with a
  123. -DFORGET_MEMORY_WHEN_FREED option so that it will not try to detect
  124. multiple frees.
  125.  
  126. The same is true for the lock tracking routines, although in that case 
  127. the argument is more clear that unlocks should cause the lock tracking 
  128. entry to be discarded, because multiple unlocks are common and multiple 
  129. locks and unlocks of the same file  during execution are also conceivably 
  130. pretty common.  Right now by default, the tracker will track locks after
  131. they have been freed.  To change this behavior, recompile tracker.c with
  132. the -DFORGET_LOCKS_WHEN_UNLOCKED option.
  133.  
  134. Unfortunately, the tracker macros that redefine AllocMem and such will
  135. cause your compiler to barf on any files you have that declare them
  136. as external.  If that happens, either remove the external declarations
  137. (and include <functions.h>) or move them to be before the include of
  138. tracker.h.
  139.  
  140.  
  141. ALPHA RELEASE, SOFTWARE STATUS
  142. ------------------------------
  143.  
  144. The Lock, Unlock and DupLock tracking routines have not been tested
  145. adequately.  The signal stuff works OK, but that's no biggie.  The
  146. main thing of interest is the tracking AllocMem and FreeMem, which
  147. I have used successfully on several programs that I have been working
  148. on.
  149.  
  150. -karl @ The Hacker's Haven, Houston, TX -- 5-Jan-89
  151.  
  152. P.S.  Note that TrackerExitReport() must be called to get the tracking
  153.  routines to free the memory they have allocated, so it's a good idea
  154.  to call it from your abnormal exit (_abort, etc) routines as well as
  155.  normal exit.  Also, that's good because you can make sure you're freeing
  156.  properly from your strange abort conditions, a thing that's hard to get
  157.  right.
  158.  
  159. SHAR_EOF
  160. cat << \SHAR_EOF > tracker.h
  161. /* tracking macros to use tracker routines */
  162.  
  163. #define AllocMem(x,y) TrackingAllocMem(x,y,__FILE__,__LINE__)
  164. #define FreeMem(x,y) TrackingFreeMem(x,y,__FILE__,__LINE__)
  165.  
  166. #define AllocSignal(x) TrackingAllocSignal(x,__FILE__,__LINE__)
  167. #define FreeSignal(x) TrackingFreeSignal(x,__FILE__,__LINE__)
  168.  
  169. #define Lock(x,y) TrackingLock(x,__FILE__,__LINE__)
  170. #define UnLock(x,y) TrackingUnLock(x,__FILE__,__LINE__)
  171. #define DupLock(x) TrackingDupLock(x,__FILE__,__LINE__)
  172.  
  173. void *TrackingAllocMem();
  174. void TrackingFreeMem();
  175.  
  176. long TrackingAllocSignal();
  177. void TrackingFreeSignal();
  178.  
  179. void *TrackingAllocRaster();
  180. void TrackingFreeRaster();
  181.  
  182. struct FileLock *TrackingLock();
  183. void TrackingUnLock();
  184. SHAR_EOF
  185. cat << \SHAR_EOF > tracker.c
  186. /* tracking memory allocator */
  187.  
  188. #include <exec/types.h>
  189. #include <exec/memory.h>
  190. #include <functions.h>
  191. #include <stdio.h>
  192.  
  193. /* comment out the following line if you want locks freed twice reported
  194.  * at the cost of getting spurious resource error messages when
  195.  * reusing the lock */
  196. /* #define FORGET_LOCKS_WHEN_UNLOCKED */
  197.  
  198. /* comment out the following line if you want memory freed twice reported
  199.  * at the cost of getting spurious resource error messages when
  200.  * freeing and reallocating memory */
  201. /* #define FORGET_MEMORY_WHEN_FREED */
  202.  
  203. /* make sure our invocations of the real routines on behalf of the user
  204.    don't cause us to recurse */
  205.  
  206. #ifdef AllocMem
  207. #undef AllocMem
  208. #undef FreeMem
  209. #undef AllocSignal
  210. #undef FreeSignal
  211. #undef Lock
  212. #undef UnLock
  213. #undef DupLock
  214. #endif
  215.  
  216. /* my flags */
  217. #define FREED_IT 1
  218.  
  219. struct TrackingAllocMemData
  220. {
  221.     UBYTE *where;            /* address returned by allocator */
  222.     long amount;            /* number of bytes allocated */
  223.     long alloc_flags;        /* flags passed to allocator */
  224.     char *file;                /* filename of caller from the macro */
  225.     int line;                /* line number of caller from macro */
  226.     long my_flags;            /* flags internal to tracker */
  227.     struct TrackingAllocMemData *next;    /* pointer to next entry */
  228. };
  229.  
  230. struct TrackingAllocMemData *TrackingAllocMemList = NULL;
  231. long MemAllocCount = 0, MemFreeCount = 0;
  232.  
  233. void *TrackingAllocMem(amount,flags,file,line)
  234. long amount;
  235. long flags;
  236. char *file;
  237. int line;
  238. {
  239.     register struct TrackingAllocMemData *rp;
  240.     UBYTE *users_memory;
  241.  
  242.     /* perform the actual alloc */
  243.     users_memory = AllocMem(amount,flags);
  244.  
  245.     /* if it succeeded, record tracking info */
  246.     if (users_memory)
  247.     {
  248.         MemAllocCount++;
  249.  
  250.         if ((rp = AllocMem((long)sizeof(struct TrackingAllocMemData),0L)) == NULL)
  251.             panic("tracker: can't alloc memory to record AllocMem data");
  252.  
  253.         /* add new alloc data entry to linked list */
  254.         rp->next = TrackingAllocMemList;
  255.         TrackingAllocMemList = rp;
  256.  
  257.         /* shove in save values */
  258.         rp->amount = amount;
  259.         rp->alloc_flags = flags;
  260.         rp->where = users_memory;
  261.         rp->file = file;
  262.         rp->line = line;
  263.         rp->my_flags = 0;
  264.     }
  265.     /* return pointer to the space allocated */
  266.     return(users_memory);
  267. }
  268.  
  269. void TrackingFreeMem(where,amount,file,line)
  270. UBYTE *where;
  271. long amount;
  272. char *file;
  273. int line;
  274. {
  275.     register struct TrackingAllocMemData *rp, *op, *freep;
  276.  
  277.     MemFreeCount++;
  278.     /* scan the memory tracking list for a match */
  279.     for (rp = TrackingAllocMemList, op = NULL; rp != NULL; op = rp, rp = rp->next)
  280.     {
  281.         /* if we matched the address */
  282.         if (rp->where == where)
  283.         {
  284.             /* if they got the amount wrong, tell them */
  285.             if (rp->amount != amount)
  286.             {
  287.                 fprintf(stderr,"freed addr %lx OK but length differs, talloc'ed %ld, freed %ld,\n\tallocated at file %s line %d, freed at file %s line %d\n",
  288.                 where,rp->amount,amount,rp->file,rp->line,file,line);
  289.             }
  290. #ifndef FORGET_MEMORY_WHEN_FREED
  291.             /* if it's already free, tell them they freed twice */
  292.             if (rp->my_flags & FREED_IT)
  293.             {
  294.                 fprintf(stderr,"freed memory twice at %lx, amount %ld,\n\tallocated in file %s at line %d, freed in file %s at line %d\n",where,amount,rp->file,rp->line,file,line);
  295.                 return;
  296.             }
  297.             else
  298.             {
  299.                 /* mark this entry as free */
  300.                 rp->my_flags |= FREED_IT;
  301.             }
  302. #else
  303.             /* remove entry from linked list and free it */
  304.             if (op != NULL) op->next = rp->next;
  305.             else
  306.                 TrackingAllocMemList = rp->next;
  307.             freep = rp;
  308.             rp = rp->next;
  309.             FreeMem(freep,(long)sizeof(struct TrackingAllocMemData));
  310. #endif
  311.             FreeMem(where,amount);
  312.  
  313.             return;
  314.         }
  315.     }
  316.     fprintf(stderr,"Freed memory at %lx of amount %ld that wasn't allocated,\n\tfreed at file %s line %d\n",where,amount,file,line);
  317.     FreeMem(where,amount);
  318. }
  319.  
  320. void ReportUnfreedMemory()
  321. {
  322.     struct TrackingAllocMemData *rp = TrackingAllocMemList, *freep;
  323.  
  324.     while (rp != NULL)
  325.     {
  326.         if (!(rp->my_flags & FREED_IT))
  327.         {
  328.             fprintf(stderr,"FreeMem was never called for memory at %lx, amount %ld,\n\tthe alloc was performed at file %s line %d\n",rp->where,rp->amount,rp->file,rp->line);
  329.         }
  330.         freep = rp;
  331.         rp = rp->next;
  332.         FreeMem(freep,(long)sizeof(struct TrackingAllocMemData));
  333.     }
  334.     printf("Total tracked AllocMem calls %ld, FreeMem calls %ld\n",MemAllocCount,MemFreeCount);
  335. }
  336.  
  337.  
  338. /* track signals */
  339. /* tracking AllocSignal doesn't currently track where it was called from */
  340.  
  341. long TrackingSignalMask = 0;
  342. long SignalAllocCount = 0, SignalFreeCount = 0;
  343.  
  344. long TrackingAllocSignal(signal_num,file,line)
  345. long signal_num;
  346. char *file;
  347. int line;
  348. {
  349.     SignalAllocCount++;
  350.  
  351.     signal_num = AllocSignal(signal_num);
  352.  
  353.     if (signal_num != -1)
  354.         TrackingSignalMask |= (1 << signal_num);
  355.  
  356.     return(signal_num);
  357. }
  358.  
  359. void TrackingFreeSignal(signal_num,file,line)
  360. long signal_num;
  361. char *file;
  362. int line;
  363. {
  364.     SignalFreeCount++;
  365.  
  366.     if (!(TrackingSignalMask & (1 << signal_num)))
  367.     {
  368.         fprintf("freed a signal (%ld) that was never allocated, at file %s line %d\n",
  369.             signal_num,file,line);
  370.         TrackingSignalMask &= ~(1 << signal_num);
  371.     }
  372. }
  373.  
  374. void ReportUnfreedSignals()
  375. {
  376.     if (TrackingSignalMask)
  377.         fprintf("failed to free signals indicated by this mask: %8lx\n",
  378.             TrackingSignalMask);
  379.     printf("Total tracked AllocSignal calls %ld, FreeSignal calls %ld\n",SignalAllocCount,SignalFreeCount);
  380. }
  381.  
  382. /* tracking lock and unlock */
  383.  
  384. struct TrackingLockData
  385. {
  386.     struct FileLock *lock;    /* lock returned by Lock */
  387.     char *name;                /* name of file that was locked */
  388.     long accessMode;        /* access mode of the file that was locked */
  389.     char *file;                /* ptr to file name of line of caller */
  390.     int line;                /* ptr to line number text of locker */
  391.     long my_flags;            /* flags internal to tracker */
  392.     struct TrackingLockData *next;    /* pointer to next entry */
  393. };
  394.  
  395. /* flags */
  396. #define CREATED_BY_DUPLOCK 1
  397.  
  398. struct TrackingLockData *TrackingLockList = NULL;
  399. long TrackerLockCount = 0, TrackerUnLockCount = 0;
  400.  
  401. struct FileLock *TrackingLock(name, accessMode, file, line)
  402. char *name;
  403. long accessMode;
  404. char *file;
  405. int line;
  406. {
  407.     register struct TrackingLockData *lp;
  408.     struct FileLock *users_lock;
  409.  
  410.     users_lock = Lock(name, (long)accessMode);
  411.  
  412.     if (users_lock)
  413.     {
  414.         TrackerLockCount++;
  415.  
  416.         if ((lp = AllocMem((long)sizeof(struct TrackingLockData),0L)) == NULL)
  417.             panic("tracker: can't alloc memory to record lock data");
  418.  
  419.         /* add new alloc data entry to linked list */
  420.         lp->next = TrackingLockList;
  421.         TrackingLockList = lp;
  422.  
  423.         /* shove in save values */
  424.         lp->accessMode = accessMode;
  425.         lp->file = file;
  426.         lp->line = line;
  427.         lp->my_flags = 0;
  428.         lp->lock = users_lock;
  429.  
  430.         /* alloc space for filename and save */
  431.         if ((lp->name = AllocMem((long)(strlen(name)+1),0L)) == NULL)
  432.             panic("tracker: can't alloc memory to record lock filename");
  433.         strcpy(lp->name,name);
  434.     }
  435.     return(users_lock);
  436. }
  437.  
  438. struct FileLock *TrackingDupLock(lock, file, line)
  439. struct FileLock *lock;
  440. char *file;
  441. int line;
  442. {
  443.     register struct TrackingLockData *lp;
  444.     struct FileLock *users_lock;
  445.  
  446.     users_lock = DupLock(lock);
  447.  
  448.     if (users_lock)
  449.     {
  450.         TrackerLockCount++;
  451.  
  452.         if ((lp = AllocMem((long)sizeof(struct TrackingLockData),0L)) == NULL)
  453.             panic("tracker: can't alloc memory to record lock data");
  454.  
  455.         /* add new alloc data entry to linked list */
  456.         lp->next = TrackingLockList;
  457.         TrackingLockList = lp;
  458.  
  459.         lp->file = file;
  460.         lp->line = line;
  461.         lp->name = NULL;
  462.         lp->lock = users_lock;
  463.         lp->my_flags = CREATED_BY_DUPLOCK;            
  464.     }
  465.     return(users_lock);
  466. }
  467.  
  468. void TrackingUnLock(lock,file,line)
  469. struct FileLock *lock;
  470. char *file;
  471. int line;
  472. {
  473.     register struct TrackingLockData *lp, *op, *freep;
  474.  
  475.     TrackerUnLockCount++;
  476.  
  477.     /* scan the lock tracking list for a match */
  478.     for (lp = TrackingLockList, op = NULL; lp != NULL; op = lp, lp = lp->next)
  479.     {
  480.         /* if we matched the lock */
  481.         if (lp->lock == lock)
  482.         {
  483. #ifndef FORGET_LOCKS_WHEN_UNLOCKED
  484.             /* if it's already free, tell them they freed twice */
  485.             if (lp->my_flags & FREED_IT)
  486.             {
  487.                 fprintf(stderr,"freed lock twice, lock %lx, filename %s\n\tlocked at file %s line %d, freed at file %s line %d\n",lock,lp->name,lp->file,lp->line,file,line);
  488.                 return;
  489.             }
  490.             else
  491.             {
  492.                 /* mark this entry as free */
  493.                 lp->my_flags |= FREED_IT;
  494.             }
  495. #else
  496.             if (op != NULL) op->next = lp->next;
  497.             else TrackingLockList = lp->next;
  498.             freep = lp;
  499.             lp = lp->next;
  500.             if (lp->name != NULL)
  501.                 FreeMem(lp->name,(long)(strlen(lp->name)+1));
  502.             FreeMem(freep,(long)(sizeof(struct TrackingLockData)));
  503. #endif
  504.             UnLock(lock);
  505.             return;
  506.         }
  507.     }
  508.     fprintf(stderr,"Freed lock %lx that hadn't been allocated at file %s line %d\n",lock,file,line);
  509. }
  510.  
  511. ReportUnfreedLocks()
  512. {
  513.     struct TrackingLockData *lp = TrackingLockList, *freep;
  514.  
  515.     while (lp != NULL)
  516.     {
  517.         if (!(lp->my_flags & FREED_IT))
  518.         {
  519.             if (lp->my_flags & CREATED_BY_DUPLOCK)
  520.             {
  521.                 fprintf(stderr,"UnLock was never called for lock %lx,\n\It was created by DupLock at file %s line %d\n",lp->lock,lp->file,lp->line);
  522.             }
  523.             else
  524.             {
  525.                 fprintf(stderr,"UnLock was never called for lock %lx,\n\It was created by a Lock of %s\nat file %s line %d\n",lp->lock,lp->name,lp->file,lp->line);
  526.             }
  527.         }
  528.         if (lp->name != NULL)
  529.             FreeMem(lp->name,(long)(strlen(lp->name)+1));
  530.         freep = lp;
  531.         lp = lp->next;
  532.         FreeMem(freep,(long)sizeof(struct TrackingLockData));
  533.     }
  534.     printf("Total tracked Lock and DupLock calls %ld, UnLock calls %ld\n",TrackerLockCount,TrackerUnLockCount);
  535. }
  536.  
  537. TrackerExitReport()
  538. {
  539.     ReportUnfreedMemory();
  540.     ReportUnfreedLocks();
  541.     ReportUnfreedSignals();
  542. }
  543.  
  544. SHAR_EOF
  545. #    End of shell archive
  546. exit 0
  547. -- 
  548. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  549. Have five nice days.
  550.